static_cast<v8::BackingStore*>(backing_store.release()));
}
+std::unique_ptr<v8::BackingStore> v8::ArrayBuffer::NewBackingStoreForNodeLTS(
+ Isolate* v8_isolate, size_t byte_length) {
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
+ API_RCS_SCOPE(i_isolate, ArrayBuffer, NewBackingStore);
+ CHECK_LE(byte_length, i::JSArrayBuffer::kMaxByteLength);
+ ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
+ std::unique_ptr<i::BackingStoreBase> backing_store =
+ i::BackingStore::Allocate(i_isolate, byte_length,
+ i::SharedFlag::kNotShared,
+ i::InitializedFlag::kUninitialized);
+ if (!backing_store) {
+ return nullptr;
+ }
+ return std::unique_ptr<v8::BackingStore>(
+ static_cast<v8::BackingStore*>(backing_store.release()));
+}
+
std::unique_ptr<v8::BackingStore> v8::ArrayBuffer::NewBackingStore(
void* data, size_t byte_length, v8::BackingStore::DeleterCallback deleter,
void* deleter_data) {
hexWrite,
ucs2Write,
utf8Write,
- getZeroFillToggle,
+ createUnsafeArrayBuffer,
} = internalBinding('buffer');
const {
obj[untransferable_object_private_symbol] = true;
}
-// A toggle used to access the zero fill setting of the array buffer allocator
-// in C++.
-// |zeroFill| can be undefined when running inside an isolate where we
-// do not own the ArrayBuffer allocator. Zero fill is always on in that case.
-let zeroFill = getZeroFillToggle();
function createUnsafeBuffer(size) {
- zeroFill[0] = 0;
- try {
+ if (size <= 64) {
+ // Allocated in heap, doesn't call backing store anyway
+ // This is the same that the old impl did implicitly, but explicit now
return new FastBuffer(size);
- } finally {
- zeroFill[0] = 1;
}
-}
-// The connection between the JS land zero fill toggle and the
-// C++ one in the NodeArrayBufferAllocator gets lost if the toggle
-// is deserialized from the snapshot, because V8 owns the underlying
-// memory of this toggle. This resets the connection.
-function reconnectZeroFillToggle() {
- zeroFill = getZeroFillToggle();
+ return new FastBuffer(createUnsafeArrayBuffer(size));
}
module.exports = {
createUnsafeBuffer,
readUInt16BE,
readUInt32BE,
- reconnectZeroFillToggle,
};
using v8::SharedArrayBuffer;
using v8::String;
using v8::Uint32;
-using v8::Uint32Array;
using v8::Uint8Array;
using v8::Value;
realm->set_buffer_prototype_object(proto);
}
-void GetZeroFillToggle(const FunctionCallbackInfo<Value>& args) {
- Environment* env = Environment::GetCurrent(args);
- NodeArrayBufferAllocator* allocator = env->isolate_data()->node_allocator();
- Local<ArrayBuffer> ab;
- // It can be a nullptr when running inside an isolate where we
- // do not own the ArrayBuffer allocator.
- if (allocator == nullptr) {
- // Create a dummy Uint32Array - the JS land can only toggle the C++ land
- // setting when the allocator uses our toggle. With this the toggle in JS
- // land results in no-ops.
- ab = ArrayBuffer::New(env->isolate(), sizeof(uint32_t));
- } else {
- uint32_t* zero_fill_field = allocator->zero_fill_field();
- std::unique_ptr<BackingStore> backing =
- ArrayBuffer::NewBackingStore(zero_fill_field,
- sizeof(*zero_fill_field),
- [](void*, size_t, void*) {},
- nullptr);
- ab = ArrayBuffer::New(env->isolate(), std::move(backing));
- }
-
- ab->SetPrivate(
- env->context(),
- env->untransferable_object_private_symbol(),
- True(env->isolate())).Check();
-
- args.GetReturnValue().Set(Uint32Array::New(ab, 0, 1));
-}
-
void DetachArrayBuffer(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (args[0]->IsArrayBuffer()) {
memcpy(dest, src, bytes_to_copy);
}
+// Converts a number parameter to size_t suitable for ArrayBuffer sizes
+// Could be larger than uint32_t
+// See v8::internal::TryNumberToSize and v8::internal::NumberToSize
+inline size_t CheckNumberToSize(Local<Value> number) {
+ CHECK(number->IsNumber());
+ double value = number.As<Number>()->Value();
+ // See v8::internal::TryNumberToSize on this (and on < comparison)
+ double maxSize = static_cast<double>(std::numeric_limits<size_t>::max());
+ CHECK(value >= 0 && value < maxSize);
+ size_t size = static_cast<size_t>(value);
+#ifdef V8_ENABLE_SANDBOX
+ CHECK_LE(size, kMaxSafeBufferSizeForSandbox);
+#endif
+ return size;
+}
+
+void CreateUnsafeArrayBuffer(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args);
+ if (args.Length() != 1) {
+ env->ThrowRangeError("Invalid array buffer length");
+ return;
+ }
+
+ size_t size = CheckNumberToSize(args[0]);
+
+ Isolate* isolate = env->isolate();
+
+ Local<ArrayBuffer> buf;
+
+ NodeArrayBufferAllocator* allocator = env->isolate_data()->node_allocator();
+ // 0-length, or zero-fill flag is set, or building snapshot
+ if (size == 0 || per_process::cli_options->zero_fill_all_buffers ||
+ allocator == nullptr) {
+ buf = ArrayBuffer::New(isolate, size);
+ } else {
+ std::unique_ptr<BackingStore> store =
+ ArrayBuffer::NewBackingStoreForNodeLTS(isolate, size);
+ if (!store) {
+ // This slightly differs from the old behavior,
+ // as in v8 that's a RangeError, and this is an Error with code
+ return env->ThrowRangeError("Array buffer allocation failed");
+ }
+ buf = ArrayBuffer::New(isolate, std::move(store));
+ }
+
+ args.GetReturnValue().Set(buf);
+}
+
void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
SetMethod(context, target, "detachArrayBuffer", DetachArrayBuffer);
SetMethod(context, target, "copyArrayBuffer", CopyArrayBuffer);
+ SetMethodNoSideEffect(
+ context, target, "createUnsafeArrayBuffer", CreateUnsafeArrayBuffer);
SetMethod(context, target, "swap16", Swap16);
SetMethod(context, target, "swap32", Swap32);
SetMethod(context, target, "hexWrite", StringWrite<HEX>);
SetMethod(context, target, "ucs2Write", StringWrite<UCS2>);
SetMethod(context, target, "utf8Write", StringWrite<UTF8>);
-
- SetMethod(context, target, "getZeroFillToggle", GetZeroFillToggle);
}
} // anonymous namespace
registry->Register(StringWrite<HEX>);
registry->Register(StringWrite<UCS2>);
registry->Register(StringWrite<UTF8>);
- registry->Register(GetZeroFillToggle);
registry->Register(DetachArrayBuffer);
registry->Register(CopyArrayBuffer);
+ registry->Register(CreateUnsafeArrayBuffer);
registry->Register(Atob);
registry->Register(Btoa);